<template>
  <q-field ref="QxDate" v-model="dateVal" :class="fieldCls" :disable="disable" :error="!isValid"
           :error-message="placeholder" :readonly="readonly" class="q-field__text" lazy-rules="ondemand"
           v-bind="$attrs">
    <template v-slot:control>
      <span v-if="!showText" class="placeholder">{{ placeholder }}</span>
      <QxTooltip v-else>{{ showText }}</QxTooltip>
    </template>
    <template v-slot:append>
      <q-icon v-if="showClearIcon" class="field-clear-icon cursor-pointer" name="cancel"
              @click.stop="handleClear"></q-icon>
      <q-icon name="event"></q-icon>
    </template>
    <q-popup-proxy v-model="showPopup" :no-parent-event="readonly || disable" :offset="[0, 5]" anchor="bottom left"
                   breakpoint="0" no-focus no-refocus self="top left" transition-hide="jump-up"
                   transition-show="jump-down" @hide="popupBlur">
      <template v-if="type?.includes('time')">
        <div class="qx-date-time full-width row justify-between q-pa-sm">
          <q-field dense outlined>
            <template v-slot:control>
              <span v-if="!startDate" class="placeholder">{{ '选择日期' }}</span>
              <QxTooltip>{{ startDate }}</QxTooltip>
            </template>
          </q-field>
          <QxTime v-model="startTime" :clearable="false" dense hideIcon outlined placeholder="选择时间"
                  @change="updateStartTime"></QxTime>
          <template v-if="range">
            <q-field dense outlined>
              <template v-slot:control>
                <span v-if="!startDate" class="placeholder">{{ '选择日期' }}</span>
                <QxTooltip>{{ endDate }}</QxTooltip>
              </template>
            </q-field>
            <QxTime v-model="endTime" :clearable="false" dense hideIcon outlined placeholder="选择时间"
                    @change="updateEndTime"></QxTime>
          </template>
        </div>
      </template>
      <q-date v-model="dateVal" :options="showPopup && options" :text-color="BgToColor('primary')" first-day-of-week="1"
              minimal v-bind="dateBind" @update:model-value="updateValue" @range-start="rangeStart"></q-date>
    </q-popup-proxy>
  </q-field>
</template>

<script setup>
import QxTime from 'components/Global/Form/QxTime.vue'
import QxTooltip from 'components/Global/Tools/QxTooltip.vue'
import { date } from 'quasar'
import { getTypeof, isEmpty } from 'src/utils/utils'
import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'

const props = defineProps({
  type: String, isLimit: Boolean, disable: Boolean, readonly: Boolean, multiple: Boolean, placeholder: String,
  option: [Array, Function], lazyRules: [Boolean, String], modelValue: [String, Date, Array, Object],
  clearable: { type: Boolean, default: true }
})

const BgToColor = inject('BgToColor')
const emits = defineEmits(['change', 'update:modelValue'])

const endTime = ref('')
const QxDate = ref(null)
const startTime = ref('')
const isValid = ref(true)
const dateVal = ref(null)
const showPopup = ref(false)
const rangeStartDate = ref('')

const showClearIcon = computed(() => {
  const { clearable, disable, readonly } = props
  if (readonly || disable || !clearable) return false
  return !!dateVal.value
})
const range = computed(() => (props.type || '').includes('range'))
const fieldCls = computed(() => showPopup.value ? 'q-field--focused q-field--highlighted' : '')
const mask = computed(() => (props.type || '').includes('time') ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD')
const options = computed(() => val => {
  const { option, isLimit } = props
  if (!option) return true
  if (typeof option !== 'function') return option
  if (!isLimit || !range.value || !rangeStartDate.value) return option(val)
  return option(val, rangeStartDate.value)
})
const dateBind = computed(() => {
  const { multiple } = props, noUnset = !(multiple || range.value)
  return { 'today-btn': true, multiple, range: range.value, mask: mask.value, noUnset, flat: true }
})
const endDate = computed(() => {
  if (isEmpty(dateVal.value)) return ''
  const SD = dateVal.value?.to || dateVal.value
  return SD && date.formatDate(SD, 'YYYY-MM-DD')
})
const startDate = computed(() => {
  if (isEmpty(dateVal.value)) return ''
  const SD = dateVal.value?.from || dateVal.value
  return SD && date.formatDate(SD, 'YYYY-MM-DD')
})
const showText = computed(() => {
  if (isEmpty(dateVal.value)) return ''
  if (getTypeof(dateVal.value) === 'array') {
    if (!range.value) return dateVal.value.join(', ')
    let str = ''
    for (const el of dateVal.value) str += `${el.from || 'NA'}/${el.to || 'NA'}, `
    return str.slice(0, -2)
  }
  if (getTypeof(dateVal.value) === 'object') {
    if (dateVal.value.from === dateVal.value.to) return dateVal.value.from
    return `${dateVal.value.from || 'NA'}/${dateVal.value.to || 'NA'}`
  }
  return dateVal.value
})

const initValue = () => {
  const { modelValue } = props
  if (getTypeof(modelValue) === 'object' && modelValue.from === modelValue.to && dateVal.value ===
    modelValue.from) return
  if (dateVal.value === modelValue) return
  if (isEmpty(modelValue)) return (dateVal.value = null)
  if (getTypeof(modelValue) === 'date') {
    dateVal.value = date.formatDate(modelValue, mask.value)
    startTime.value = date.formatDate(modelValue, 'HH:mm:ss')
    emits('update:modelValue', dateVal.value || '')
    return
  }

  if (getTypeof(modelValue) === 'array') {
    dateVal.value = modelValue.map(el => {
      if (getTypeof(el) === 'date') return date.formatDate(el, mask.value)
      if (getTypeof(el) === 'object') {
        const form = {}
        if (getTypeof(el.from) === 'date') form.from = date.formatDate(el.from, mask.value)
        else form.from = el.from
        if (getTypeof(el.to) === 'date') form.to = date.formatDate(el.to, mask.value)
        else form.to = el.to
        return form.from === form.to ? form.to : form
      }
      return el
    })
    emits('update:modelValue', dateVal.value || '')
    return
  }

  if (getTypeof(modelValue) === 'object') {
    const from = getTypeof(modelValue.from) === 'date' ? date.formatDate(modelValue.from, mask.value) : modelValue.from
    const to = getTypeof(modelValue.to) === 'date' ? date.formatDate(modelValue.to, mask.value) : modelValue.to
    startTime.value = date.formatDate(from, 'HH:mm:ss')
    endTime.value = date.formatDate(to, 'HH:mm:ss')
    if (from === to) {
      dateVal.value = from
      emits('update:modelValue', { from, to })
      return
    } else dateVal.value = { from, to }
    emits('update:modelValue', dateVal.value)
    return
  }
  dateVal.value = modelValue
  startTime.value = date.formatDate(modelValue, 'HH:mm:ss')
}

watch(() => props.modelValue, () => initValue(), { immediate: true })

const popupBlur = () => {
  if (!props.multiple && dateVal.value && typeof dateVal.value === 'string' && range.value) {
    emits('update:modelValue', { from: dateVal.value, to: dateVal.value })
  } else emits('update:modelValue', dateVal.value || '')
  rangeStartDate.value = ''
  if (props.lazyRules === 'ondemand') return
  nextTick(() => {
    if (!QxDate.value) return
    QxDate.value.blur()
    isValid.value = QxDate.value.validate()
  })
}
const updateStartTime = value => {
  startTime.value = value
  if (isEmpty(dateVal.value)) {
    const s = `${date.formatDate(new Date(), 'YYYY-MM-DD')} ${value}`
    if (range.value) {
      dateVal.value = { from: s, to: s }
      emits('change', dateVal.value)
      return
    }
    dateVal.value = s
    emits('change', dateVal.value)
    return
  }
  if (getTypeof(dateVal.value) === 'Object') {
    dateVal.value.from = `${startDate.value} ${value}`
    emits('change', dateVal.value)
    return
  }
  dateVal.value = `${startDate.value} ${value}`
  emits('change', dateVal.value)
}
const updateEndTime = value => {
  endTime.value = value
  if (isEmpty(dateVal.value)) {
    const s = `${date.formatDate(new Date(), 'YYYY-MM-DD')} ${value}`
    if (range.value) {
      dateVal.value = { from: s, to: s }
      emits('change', dateVal.value)
      return
    }
    dateVal.value = s
    emits('change', dateVal.value)
    return
  }
  if (getTypeof(dateVal.value) === 'Object') {
    dateVal.value.to = `${startDate.value} ${value}`
    emits('change', dateVal.value)
    return
  }
  dateVal.value = `${startDate.value} ${value}`
  emits('change', dateVal.value)
}

const rangeStart = from => {
  const { option, isLimit } = props
  if (!isLimit || !range.value || !option || typeof option !== 'function') return
  rangeStartDate.value = date.formatDate(date.buildDate(from), 'YYYY/MM/DD')
}
const updateValue = value => {
  if (!value) return emits('change', value)
  if (props.type?.includes('time')) {
    endTime.value = endTime.value || '23:59:59'
    startTime.value = startTime.value || '00:00:00'
    if (getTypeof(dateVal.value) === 'Object') {
      dateVal.value.from = `${startDate.value} ${startTime.value || '00:00:00'}`
      dateVal.value.to = `${endDate.value} ${startTime.value || '23:59:59'}`
      emits('change', dateVal.value)
      return
    }
    dateVal.value = `${startDate.value} ${startTime.value || '00:00:00'}`
    emits('change', dateVal.value)
    return
  }
  emits('change', value)
  if (!props.multiple) showPopup.value = false
}
const handleClear = () => {
  dateVal.value = null
  endTime.value = ''
  startTime.value = ''
  showPopup.value = false
  popupBlur()
}

const scrollHandle = () => {
  if (!showPopup.value || !QxDate.value) return
  const offset = QxDate.value.$el.getBoundingClientRect()
  const innerHeight = window.innerHeight
  if (offset.top > innerHeight || offset.bottom < 0) showPopup.value = false
}
const resizeHandle = () => (showPopup.value = false)
onMounted(() => {
  window.addEventListener('scroll', scrollHandle, true)
  window.addEventListener('resize', resizeHandle, false)
})
onBeforeUnmount(() => {
  window.removeEventListener('scroll', scrollHandle, true)
  window.removeEventListener('resize', resizeHandle, false)
})

</script>